package com.spring;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class HongApplicationContext {

    private ConcurrentMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap();
    // 单例池
    private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap();
    // 扩展
    private List<BeanPostProcessor> beanPostProcessorsList = new ArrayList<>();

    public HongApplicationContext(Class configClass) {
        // 扫描类
        List<Class> classList = scan(configClass);
        // 扫描到类了之后。解析这个类，Component，BeanDefinition（@Scope）
        for (Class clazz : classList) {
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setBeanClass(clazz);
            // if (clazz.isAnnotationPresent(Component.class)){}
            Component component = (Component) clazz.getAnnotation(Component.class);
            // 存在这个注解，证明就是个bean,拿到bean的名字
            String beanName = component.value();
            System.out.println("beanName="+beanName);
            if (clazz.isAnnotationPresent(Scope.class)){
                Scope scope = (Scope) clazz.getAnnotation(Scope.class);
                beanDefinition.setScope(scope.value());
            }else {
                beanDefinition.setScope("singleton");
            }
            // clazz是不是BeanPostProcessor派生出来的
            if (BeanPostProcessor.class.isAssignableFrom(clazz) ){
                try {
                    BeanPostProcessor bpp = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                    beanPostProcessorsList.add(bpp);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }

            beanDefinitionMap.put(beanName,beanDefinition);
        }

        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton")){
                // 生成这个bean
                Object bean = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,bean);
            }
        }
        // 生成单例bean（非懒加载） ---> 单例池

    }

    private Object createBean(String beanName,BeanDefinition beanDefinition) {
        Class beanClass = beanDefinition.getBeanClass();
        try {
            // 实例化
            Object bean = beanClass.getDeclaredConstructor().newInstance();
            // 填充属性（依赖注入）
            Field[] fields = beanClass.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(AutoWired.class)) {
                    // 拿到属性
                    Object userService = getBean(field.getName());
                    // 赋值
                    field.setAccessible(true);
                    field.set(bean,userService);
                }
            }
            // Aware 新建一个接口 方法新加一个参数（扩展机制）
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            // 初始化之前 可以调用程序员定义的逻辑
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorsList) {
                bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
            }

            // 初始化
            if (bean instanceof InitializingBean) {
                ((InitializingBean)bean).afterPropertiesSet();
            }
            // 初始化之后 可以调用程序员定义的逻辑 可以aop
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorsList) {
                bean = beanPostProcessor.postProcessAfterInitialization(bean,beanName);
            }
            return bean;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }


    public Object getBean(String beanName){
        // 原型的话 判断是不是原型
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition.getScope().equals("prototype")){
            return createBean(beanName,beanDefinition);
        }else {
            // 单例池中去拿
            Object bean = singletonObjects.get(beanName);
            if (bean == null){
                Object o = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,o);

                return o;
            }
            return bean;
        }
    }

    private List<Class> scan(Class configClass) {
        List<Class> classList = new ArrayList<>();
        ComponmentScan componmentScan = (ComponmentScan) configClass.getAnnotation(ComponmentScan.class);
        String scanPath = componmentScan.value();  // com.hong.service  ----> com/hong/service
        // 非懒加载的单例 @Lazy @Scope
        // 生成单例bean---->单例池

        scanPath = scanPath.replace(".","/");

        // 如何扫描类
        ClassLoader classLoader = HongApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(scanPath);

        String s = null;
        try {
            s = URLDecoder.decode(resource.getFile(), "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        File file = new File(s);  // 目录

        // 指定的目录下的文件列表，可能是class,可能是txt
        File[] files = file.listFiles();

        for (File f : files) {
            String absolutePath = f.getAbsolutePath();
            absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"))
                    .replace("\\", ".");
            System.out.println("absolutePath="+absolutePath);
            // class文件加载进来
            try {
                Class<?> clazz = classLoader.loadClass(absolutePath);

                classList.add(clazz);

            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return classList;
    }

}
